/***********************************************************************
* Microsoft (R) Windows (R) Resource Compiler
*
* Copyright (c) Microsoft Corporation.  All rights reserved.
*
* File Comments:
*
*
***********************************************************************/

#include "rc.h"
#include "assert.h"


#define MAXCODE    128000 //AFX uses > 65000
#define DIBBITMAPFORMAT   0x4D42   /* 'BM' as in PM format */

#undef  min
#define min(a,b) ((a<b)?(a):(b))

PCHAR       CodeArray;      /* pointer to code buffer */
int         CodeSize;       /* size of code buffer */
int         CCount;         /* current code array address */
PFILE       fhCode;         /* file handle for remaining data */
static int  ItemCountLoc;   /* a patch location; this one for */
static int  ItemExtraLoc;   /* a patch location; this one for */

typedef struct {
    SHORT   csHotX;
    SHORT   csHotY;
    SHORT   csWidth;
    SHORT   csHeight;
    SHORT   csWidthBytes;
    SHORT   csColor;
} IconHeader;


typedef struct {
    UINT        dfVersion;              /* not in FONTINFO */
    DWORD       dfSize;                 /* not in FONTINFO */
    CHAR        dfCopyright[60];        /* not in FONTINFO */
    UINT        dfType;
    UINT        dfPoints;
    UINT        dfVertRes;
    UINT        dfHorizRes;
    UINT        dfAscent;
    UINT        dfInternalLeading;
    UINT        dfExternalLeading;
    BYTE        dfItalic;
    BYTE        dfUnderline;
    BYTE        dfStrikeOut;
    UINT        dfWeight;
    BYTE        dfCharSet;
    UINT        dfPixWidth;
    UINT        dfPixHeight;
    BYTE        dfPitchAndFamily;
    UINT        dfAvgWidth;
    UINT        dfMaxWidth;
    BYTE        dfFirstChar;
    BYTE        dfLastChar;
    BYTE        dfDefaultCHar;
    BYTE        dfBreakChar;
    UINT        dfWidthBytes;
    DWORD       dfDevice;           /* See Adaptation Guide 6.3.10 and 6.4 */
    DWORD       dfFace;             /* See Adaptation Guide 6.3.10 and 6.4 */
    DWORD       dfReserved;         /* See Adaptation Guide 6.3.10 and 6.4 */
} ffh;

#define FONT_FIXED sizeof(ffh)
#define FONT_ALL sizeof(ffh) + 64

struct MacCursor {
    char data[16];
    char mask[16];
    short hotSpotV;
    short hotSpotH;
};

typedef struct {
    unsigned short red;
    unsigned short green;
    unsigned short blue;
} RGBColor;

typedef struct {
    unsigned short value;
    RGBColor rgb;
} ColorSpec;


#define ccs2 2
const ColorSpec rgcs2[ccs2] = {
    {0, {0xffff,0xffff,0xffff}},
    {1, {0x0000,0x0000,0x0000}}
};


#define ccs16 16
const ColorSpec rgcs16[ccs16] = {
    {0x00, {0xffff,0xffff,0xffff}},
    {0x01, {0xfc00,0xf37d,0x052f}},
    {0x02, {0xffff,0x648a,0x028c}},
    {0x03, {0xdd6b,0x08c2,0x06a2}},
    {0x04, {0xf2d7,0x0856,0x84ec}},
    {0x05, {0x46e3,0x0000,0xa53e}},
    {0x06, {0x0000,0x0000,0xd400}},
    {0x07, {0x0241,0xab54,0xeaff}},
    {0x08, {0x1f21,0xb793,0x1431}},
    {0x09, {0x0000,0x64af,0x11b0}},
    {0x0a, {0x5600,0x2c9d,0x0524}},
    {0x0b, {0x90d7,0x7160,0x3a34}},
    {0x0c, {0xc000,0xc000,0xc000}},
    {0x0d, {0x8000,0x8000,0x8000}},
    {0x0e, {0x4000,0x4000,0x4000}},
    {0x0f, {0x0000,0x0000,0x0000}}
};


/*
 *  the 34 legal icon colors
 */
#define ccs256 34
const ColorSpec rgcs256[ccs256] = {
    {0x01, {0xFFFF, 0xFFFF, 0xCCCC}},
    {0x08, {0xFFFF, 0xCCCC, 0x9999}},
    {0x33, {0xCCCC, 0x9999, 0x6666}},
    {0x16, {0xFFFF, 0x6666, 0x3333}},
    {0x92, {0x3333, 0xFFFF, 0x9999}},
    {0xE3, {0x0000, 0xBBBB, 0x0000}},
    {0x9F, {0x3333, 0x9999, 0x6666}},
    {0xA5, {0x3333, 0x6666, 0x6666}},
    {0x48, {0x9999, 0xFFFF, 0xFFFF}},
    {0xC0, {0x0000, 0x9999, 0xFFFF}},
    {0xEC, {0x0000, 0x0000, 0xDDDD}},
    {0xB0, {0x3333, 0x0000, 0x9999}},
    {0x2A, {0xCCCC, 0xCCCC, 0xFFFF}},
    {0x54, {0x9999, 0x9999, 0xFFFF}},
    {0x7F, {0x6666, 0x6666, 0xCCCC}},
    {0xAB, {0x3333, 0x3333, 0x6666}},
    {0x13, {0xFFFF, 0x6666, 0xCCCC}},
    {0x69, {0x9999, 0x0000, 0x6666}},
    {0x5C, {0x9999, 0x6666, 0x9999}},
    {0x00, {0xFFFF, 0xFFFF, 0xFFFF}},
    {0xF5, {0xEEEE, 0xEEEE, 0xEEEE}},
    {0xF6, {0xDDDD, 0xDDDD, 0xDDDD}},
    {0x2B, {0xCCCC, 0xCCCC, 0xCCCC}},
    {0xF7, {0xBBBB, 0xBBBB, 0xBBBB}},
    {0xF8, {0xAAAA, 0xAAAA, 0xAAAA}},
    {0xF9, {0x8888, 0x8888, 0x8888}},
    {0xFA, {0x7777, 0x7777, 0x7777}},
    {0xFB, {0x5555, 0x5555, 0x5555}},
    {0xFC, {0x4444, 0x4444, 0x4444}},
    {0xFD, {0x2222, 0x2222, 0x2222}},
    {0xFE, {0x1111, 0x1111, 0x1111}},
    {0xFF, {0x0000, 0x0000, 0x0000}},
    {0x05, {0xFFFF, 0xFFFF, 0x0000}},
    {0xD8, {0xDDDD, 0x0000, 0x0000}}
};

void ProcessMacIcons(RESINFO* pRes, int itBase, int ib1, int ib4, int ib8);
void ReadDIB(int ibDesc, struct tagDESCRIPTOR *pds, BITMAPINFOHEADER* pbmh, int* pcbWidth, void** ppBits, RGBQUAD** prgrgq, BOOL fIcon);
void CompactAndFlipIcon(BYTE* pBits, int cbRowCur, int cbRowMask, int cbRowNew, int cbRowMaskNew, int Height);
void WriteMacRsrc(void* pBits, int cbBits, RESINFO* pResBase, DWORD res);
void LookupIconColor(const ColorSpec* rgcs, int ccs, RGBQUAD* pq);
long MungeResType(WCHAR *szType, short wOrd);
int IdUnique(TYPEINFO *ptype, RESINFO* pres);
RESINFO* LookupIconRes(TYPEINFO* ptypeIcon, RESINFO* pres);
void TranslateString(char* sz);
void TranslateBuffer(char* rgch, int cch);


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*      GenWarning2() -                                                      */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void
GenWarning2(
    int iMsg,
    const wchar_t *arg
    )
{
    SET_MSG(iMsg, curFile, token.row, arg);
    SendWarning(Msg_Text);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*      GenWarning4() -                                                      */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void
GenWarning4(
    int iMsg,
    const wchar_t *arg1,
    const wchar_t *arg2,
    const wchar_t *arg3
    )
{
    SET_MSG(iMsg,
            curFile,
            token.row,
            arg1,
            arg2,
            arg3);

    SendWarning(Msg_Text);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*      GenError2() -                                                        */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void
GenError2(
    int iMsg,
    const wchar_t *arg
    )
{
    if (fhCode > 0)
        fclose(fhCode);

    SET_MSG(iMsg, curFile, token.row, arg);
    SendError(Msg_Text);
    quit(NULL);
}


/*---------------------------------------------------------------------------*/
/*                                                                                                                                                       */
/*      GenError1() -                                                                                                                    */
/*                                                                                                                                                       */
/*---------------------------------------------------------------------------*/

void
GenError1(
    int iMsg
    )
{
    if (fhCode > 0)
        fclose(fhCode);

    SET_MSG(iMsg, curFile, token.row);
    SendError(Msg_Text);
    quit(NULL);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  CtlAlloc() -                                                             */
/*                                                                           */
/*---------------------------------------------------------------------------*/
VOID
CtlAlloc(
    VOID
    )
{
    CodeSize = MAXCODE;
    CodeArray = (PCHAR) MyAlloc(MAXCODE);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  CtlInit() -                                                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/
VOID
CtlInit(
    VOID
    )
{
    CCount = 0;         /* absolute location in CodeArray */
    fhCode = NULL_FILE; /* don't copy file unless we need to */
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  CtlFile() -                                                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/
PFILE
CtlFile(
    PFILE fh
    )
{
    if (fh != NULL_FILE)
        fhCode = fh;    /* set file handle to read remaining resource from */

    return(fhCode);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  CtlFree() -                                                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/
VOID
CtlFree(
    VOID
    )
{
    CodeSize = 0;
    MyFree(CodeArray);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  GetSpace() -                                                             */
/*                                                                           */
/*---------------------------------------------------------------------------*/
void *GetSpace(WORD cb)
{
    PCHAR pch;

    if (CCount > (int) (CodeSize - cb)) {
        PVOID pv = HeapReAlloc(hHeap, HEAP_NO_SERIALIZE|HEAP_ZERO_MEMORY, ((PCHAR) CodeArray) - 8, CodeSize + 0x00010000 + 8);

        if (pv == NULL) {
            GenError1(2168); //"Resource too large"
            /* GenError1 calls quit(NULL) and doesn't return! */
        }

        CodeArray = ((PCHAR) pv)+8;
        CodeSize += 0x00010000;
    }

    pch = CodeArray + CCount;
    CCount += cb;

    return(pch);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  WriteString() -                                                          */
/*                                                                           */
/*---------------------------------------------------------------------------*/
VOID
WriteString(
    PWCHAR sz,
    BOOL fMacCP
    )
{
    /* add a string to the resource buffer */
    if (fMacRsrcs) {
        WriteMacString(sz, fMacCP, FALSE);
    } else {
        do {
            WriteWord(*sz);
        } while (*sz++ != 0);
    }
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  WriteMacString() -                                                       */
/*                                                                           */
/*---------------------------------------------------------------------------*/
VOID
WriteMacString(
    PWCHAR sz,
    BOOL fMacCP,
    BOOL fPascal
    )
{
    BYTE rgb[256];
    BYTE rgbExpand[256];
    BYTE* pb = rgb;
    BYTE* pbExpand = rgbExpand;
    int cch = 0;
    int cb = 0;

    if (sz != NULL)
    {
        UINT iCP;
        UINT nCP = uiCodePage;
        BOOL fAttemptTranslate = FALSE;
        static const UINT rgnCP[] = {10029, 10007, 10000, 10006, 10081};    // Mac codepages
        static BOOL rgfCP[5];

        // If the codepage for the current resource text is one of the Windows
        // Latin 1, Greek, Cyrillic, Turkish, or Eastern European codepages, then
        // there exists a corresponding Macintosh codepage in Win32 and it is valid
        // to use WCToMB to map the Windows text to the Macintosh character set. If
        // the Windows text is in a different code page, we don't try to do any
        // mapping.
        iCP = uiCodePage - 1250;
        if (fMacCP && uiCodePage >= 1250 && uiCodePage <= 1254) {
            nCP = rgnCP[iCP];

            // unfortunately the Mac code pages are not supported under Windows 95.
            // To handle this, we check to see if the Mac code page we came up
            // with is actually available, and if it isn't, revert back to uiCodePage.

            if ((rgfCP[iCP] & 0x01) == 0) {  // is this fCP still uninitialized?
                rgfCP[iCP] |= 0x01;     // bit 0 set: has been initialized
                if (IsValidCodePage(nCP))
                    rgfCP[iCP] |= 0x02;     // bit 1 set: this CP is available
            }

            if ((rgfCP[iCP] & 0x02) == 0) {
                nCP = uiCodePage;
                fAttemptTranslate = TRUE;
            }
        }

        cch = wcslen(sz);

        cb = WideCharToMultiByte(nCP, 0, sz, cch, NULL, 0, NULL, NULL);
        if (cb > sizeof(rgb))
            pb = (BYTE *) MyAlloc(cb);
        WideCharToMultiByte(nCP, 0, sz, cch, (LPSTR) pb, cb, NULL, NULL);

        // if the Mac code page we wanted isn't available, try using our hard-coded tables
        if (fAttemptTranslate)
            TranslateBuffer((LPSTR) pb, cb);
    }

    if (fPascal) {
        WriteByte((char)cb);
        WriteBuffer(rgb, (size_t) cb);
    } else {
        // at worst, we'll need one wide char for every single-byte char, plus a null terminator
        if (((cb + 1) * sizeof(WCHAR)) > sizeof(rgbExpand))
            pbExpand = (BYTE *) MyAlloc((cb + 1) * sizeof(WCHAR));

        cb = ExpandString(pb, cb, pbExpand);
        WriteBuffer(pbExpand, (size_t) cb);

        if (pbExpand != rgbExpand)
            MyFree(pbExpand);
    }

    if (pb != rgb)
        MyFree(pb);
}


const unsigned char *mpchchCodePage;

const unsigned char mpchchLatin1ToMac[128] = {
    0x3f, /* 0x80 */
    0x3f, /* 0x81 */
    0xe2, /* 0x82 */
    0xc4, /* 0x83 */
    0xe3, /* 0x84 */
    0xc9, /* 0x85 */
    0xa0, /* 0x86 */
    0xe0, /* 0x87 */
    0xf6, /* 0x88 */
    0xe4, /* 0x89 */
    0x3f, /* 0x8a */
    0xdc, /* 0x8b */
    0xce, /* 0x8c */
    0x3f, /* 0x8d */
    0x3f, /* 0x8e */
    0x3f, /* 0x8f */
    0x3f, /* 0x90 */
    0xd4, /* 0x91 */
    0xd5, /* 0x92 */
    0xd2, /* 0x93 */
    0xd3, /* 0x94 */
    0xa5, /* 0x95 */
    0xd0, /* 0x96 */
    0xd1, /* 0x97 */
    0xf7, /* 0x98 */
    0x84, /* 0x99 */
    0x3f, /* 0x9a */
    0xdd, /* 0x9b */
    0xcf, /* 0x9c */
    0x3f, /* 0x9d */
    0x3f, /* 0x9e */
    0xd9, /* 0x9f */
    0xca, /* 0xa0 */
    0xc1, /* 0xa1 */
    0xa2, /* 0xa2 */
    0xa3, /* 0xa3 */
    0xdb, /* 0xa4 */
    0xb4, /* 0xa5 */
    0x3f, /* 0xa6 */
    0xa4, /* 0xa7 */
    0xac, /* 0xa8 */
    0xa9, /* 0xa9 */
    0xbb, /* 0xaa */
    0xc7, /* 0xab */
    0xc2, /* 0xac */
    0x3f, /* 0xad */
    0xa8, /* 0xae */
    0x3f, /* 0xaf */
    0xa1, /* 0xb0 */
    0xb1, /* 0xb1 */
    0x3f, /* 0xb2 */
    0x3f, /* 0xb3 */
    0xab, /* 0xb4 */
    0xb5, /* 0xb5 */
    0xa6, /* 0xb6 */
    0xe1, /* 0xb7 */
    0xfc, /* 0xb8 */
    0x3f, /* 0xb9 */
    0xbc, /* 0xba */
    0xc8, /* 0xbb */
    0x3f, /* 0xbc */
    0x3f, /* 0xbd */
    0x3f, /* 0xbe */
    0xc0, /* 0xbf */
    0xcb, /* 0xc0 */
    0xe7, /* 0xc1 */
    0xe5, /* 0xc2 */
    0xcc, /* 0xc3 */
    0x80, /* 0xc4 */
    0x81, /* 0xc5 */
    0xae, /* 0xc6 */
    0x82, /* 0xc7 */
    0xe9, /* 0xc8 */
    0x83, /* 0xc9 */
    0xe6, /* 0x3f */
    0xe8, /* 0xcb */
    0xed, /* 0xcc */
    0xea, /* 0xcd */
    0xeb, /* 0xce */
    0xec, /* 0xcf */
    0x3f, /* 0xd0 */
    0x84, /* 0xd1 */
    0xf1, /* 0xd2 */
    0xee, /* 0xd3 */
    0xef, /* 0xd4 */
    0xcd, /* 0xd5 */
    0x85, /* 0xd6 */
    0x3f, /* 0xd7 */
    0xaf, /* 0xd8 */
    0x84, /* 0xd9 */
    0xf2, /* 0xda */
    0xf3, /* 0xdb */
    0x86, /* 0xdc */
    0x3f, /* 0xdd */
    0x3f, /* 0xde */
    0xa7, /* 0xdf */
    0x88, /* 0xe0 */
    0x87, /* 0xe1 */
    0x89, /* 0xe2 */
    0x8b, /* 0xe3 */
    0x8a, /* 0xe4 */
    0x8c, /* 0xe5 */
    0xbe, /* 0xe6 */
    0x8d, /* 0xe7 */
    0x8f, /* 0xe8 */
    0x8e, /* 0xe9 */
    0x90, /* 0xea */
    0x91, /* 0xeb */
    0x93, /* 0xec */
    0x92, /* 0xed */
    0x94, /* 0xee */
    0x95, /* 0xef */
    0x3f, /* 0xf0 */
    0x96, /* 0xf1 */
    0x98, /* 0xf2 */
    0x97, /* 0xf3 */
    0x99, /* 0xf4 */
    0x9b, /* 0xf5 */
    0x9a, /* 0xf6 */
    0xd6, /* 0xf7 */
    0xbf, /* 0xf8 */
    0x9d, /* 0xf9 */
    0x9c, /* 0xfa */
    0x9e, /* 0xfb */
    0x9f, /* 0xfc */
    0x3f, /* 0xfd */
    0x3f, /* 0xfe */
    0xd8, /* 0xff */
};

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  BuildCodePage() -                                                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/
void
BuildCodePage(
    int cp
    )
{
    mpchchCodePage = cp == 1252 ? mpchchLatin1ToMac : NULL;
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  TranslateString() -                                                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/
void
TranslateString(
    char* sz
    )
{
    TranslateBuffer(sz, strlen(sz)+1);
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  TranslateBuffer() -                                                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/
void
TranslateBuffer(
    char* rgch,
    int cch
    )
{
    if (mpchchCodePage == NULL)
        BuildCodePage(uiCodePage);

    if (mpchchCodePage == NULL)
        return;

    for (NULL; cch > 0; rgch++, cch--)
        if (*rgch & 0x80)
            *rgch = (char) mpchchCodePage[(unsigned char)(*rgch-0x80)];
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  ExpandString() -                                                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/
int
ExpandString(
    BYTE* pb,
    int cb,
    BYTE* pbExpand
    )
{
    int cbWide = 2; // for null terminator

    while (cb > 0) {
        if (IsDBCSLeadByteEx(uiCodePage, *pb)) {
            *pbExpand++ = *pb++;
            cb--;
        } else {
            *pbExpand++ = 0;
        }

        *pbExpand++ = *pb++;
        cbWide += 2;
        cb--;
    }

    *(WORD*) pbExpand++ = L'\0';
    return cbWide;
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  AppendString() -                                                          */
/*                                                                           */
/*---------------------------------------------------------------------------*/
VOID
AppendString(
    PWCHAR sz,
    BOOL fMacCP
    )
{
    PWCHAR psz;

    /* add a string to the resource buffer */
    psz = (PWCHAR) (&CodeArray[CCount]);
    if (*(psz-1) == L'\0')
        CCount -= sizeof(WCHAR);
    WriteString(sz, fMacCP);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  WriteAlign() -                                                           */
/*                                                                           */
/*---------------------------------------------------------------------------*/
VOID
WriteAlign(
    VOID
    )
{
    WORD    i = CCount % 4;

    while (i--)
        WriteByte(0);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  WriteBuffer() -                                                          */
/*                                                                           */
/*---------------------------------------------------------------------------*/

void WriteBuffer(const void *pv, size_t cb)
{
    const BYTE *pb = (BYTE *) pv;

    while (cb--) {
        WriteByte(*pb++);
    }
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/* WriteControl() -                                                         */
/*                                                                          */
/*  Parameters:                                                             */
/*      outfh  : The handle of the RES file.                                */
/*      Array  : Pointer to array from which some data is to be copied into */
/*               the .RES file.                                             */
/*               This is ignored if ArrayCount is zero.                     */
/*      ArrayCount : This is the number of bytes to be copied from "Array"  */
/*                   into the .RES file. This is zero if no copy is required*/
/*      FileCount  : This specifies the number of bytes to be copied from   */
/*                   fhCode into fhOut. If this is -1, the complete input   */
/*                   file is to be copied into fhOut.                       */
/**/
/**/

int
WriteControl(
    PFILE outfh,
    PCHAR Array,
    int ArrayCount,
    LONG FileCount
    )
{

    /* Check if the Array is to be written to .RES file */
    if (ArrayCount > 0)
        /* write the array (resource) to .RES file */
        MyWrite(outfh, Array, ArrayCount);

    /* copy the extra input file - opened by generator functions */
    if (fhCode != NULL_FILE) {
        /* Check if the complete input file is to be copied or not */
        if (FileCount == -1) {
            MyCopyAll(fhCode, outfh);
            fclose(fhCode);
        } else {
            /* Only a part of the input file is to be copied */
            MyCopy(fhCode, outfh, FileCount);

            /* Note that the fhCode is NOT closed in this case */
        }
    }

    return(ArrayCount);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  ResourceSize() -                                                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/

LONG
ResourceSize (
    VOID
    )
{
    if (fhCode == NULL_FILE)
        return (LONG)CCount;            /* return size of array */
    else {
        /* note: currently all resources that use the fhCode file
         * compute their own resource sizes, and this shouldn't get
         * executed, but it is here in case of future modifications
         * which require it.
         */
        LONG lFPos = MySeek(fhCode, 0L, SEEK_CUR);
        LONG lcb = (LONG)CCount + MySeek(fhCode, 0L, SEEK_END) - lFPos;
        MySeek(fhCode, lFPos, SEEK_SET);
        return lcb;
    }
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  GetIcon() -                                                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/

DWORD
GetIcon(
    LONG nbyFile
    )
{
    PFILE      infh = CtlFile(NULL_FILE);

    IconHeader header;
    LONG    nbyIconSize, nSeekLoc;
    LONG    nbyTransferred = 0;
    SHORT   IconID;
    int bHeaderWritten = FALSE;

    if (infh == NULL)
        return FALSE;
    /* read the header and find its size */
    if (!MyRead( infh, &IconID, sizeof(SHORT))) {
        GenError2(2169, tokenbuf); //"Resource file %ws is not in 2.03 format."
        return FALSE;
    }

    /* Check if the input file is in correct format */
    if (((CHAR)IconID != 1) && ((CHAR)IconID != 3))
        GenError2(2169, tokenbuf); //"Resource file %ws is not in 2.03 format."

    if (!MyRead( infh, &header, sizeof(IconHeader))) {
        GenError2(2169, tokenbuf); //"Resource file %ws is not in 2.03 format."
        return FALSE;
    }
    nbyIconSize = (header.csWidthBytes * 2) * header.csHeight;

    /* if pre-shrunk version exists at eof */
    if ((nSeekLoc = ( sizeof (SHORT) + nbyIconSize + sizeof(IconHeader))) < nbyFile) {
        /* mark as device dependant */
        *(((PCHAR)&IconID) + 1) = 0;
        MySeek(infh, (LONG)nSeekLoc, SEEK_SET);
        WriteWord(IconID);
    } else {   /* only canonical version exists */

        *(((PCHAR)&IconID) + 1) = 1;   /* mark as device independent */
        WriteWord(IconID);
        WriteBuffer(&header, sizeof(IconHeader));
        bHeaderWritten = TRUE;
    }

    nbyTransferred = nbyFile - MySeek(infh, 0L, SEEK_CUR);

    /* return number of bytes in the temporary file */
    return (nbyTransferred + (bHeaderWritten ? sizeof(IconHeader) : 0)
         + sizeof(SHORT));
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  GetNewBitmap() -                                                        */
/*                                                                          */
/*   This loads the new bitmaps in DIB format (PM format)                   */
/*--------------------------------------------------------------------------*/

DWORD
GetNewBitmap(
    VOID
    )
{
    PFILE infh = CtlFile(NULL_FILE);
    BITMAPFILEHEADER bf;
    BITMAPCOREHEADER bc;
    BITMAPINFOHEADER *pBitMapInfo;
    int cbColorTable;
    PCHAR pColorTable;
    LONG        cbImage;
    int nbits;
    DWORD BitmapSize;

    if (infh == NULL)
        return FALSE;

    MyRead(infh, &bf, sizeof(bf));

    /* Check if it is in correct format */
    if (bf.bfType != DIBBITMAPFORMAT)
        GenError2(2170, tokenbuf); //"Bitmap file %ws is not in 3.00 format."

    /* get the header -- assume old format */
    MyRead(infh, &bc, sizeof(bc));

    BitmapSize = bc.bcSize;

    if (BitmapSize >= sizeof(BITMAPINFOHEADER)) {
        /* V3 or better format */
        pBitMapInfo = (BITMAPINFOHEADER *) MyAlloc(BitmapSize);

        memcpy(pBitMapInfo, &bc, sizeof(bc));

        MyRead(infh, ((PCHAR)pBitMapInfo) + sizeof(bc), BitmapSize - sizeof(bc));

        nbits = pBitMapInfo->biPlanes * pBitMapInfo->biBitCount;

        if ( pBitMapInfo->biCompression == BI_BITFIELDS ) {
            if( (pBitMapInfo->biBitCount <= 8) ||
                (pBitMapInfo->biBitCount == 24) )
            {
                GenError2(2170, tokenbuf); //"Bitmap file %ws is not in 3.00 format."
            }

            cbColorTable = 3 * sizeof(DWORD);
        } else {
            // Only pBitMapInfo->biBitCount 1,4,8,24. biBitCount 16 and 32 MUST have BI_BITFIELD specified
            cbColorTable = (int)pBitMapInfo->biClrUsed * sizeof(RGBQUAD);
            if ((cbColorTable  == 0) && (pBitMapInfo->biBitCount<=8))
                cbColorTable = (1 << nbits) * sizeof(RGBQUAD);
        }

        if (fMacRsrcs) {
            pBitMapInfo->biSize = SwapLong(pBitMapInfo->biSize);
            pBitMapInfo->biWidth = SwapLong(pBitMapInfo->biWidth);
            pBitMapInfo->biHeight = SwapLong(pBitMapInfo->biHeight);
            pBitMapInfo->biPlanes = SwapWord(pBitMapInfo->biPlanes);
            pBitMapInfo->biBitCount = SwapWord(pBitMapInfo->biBitCount);
            pBitMapInfo->biCompression = SwapLong(pBitMapInfo->biCompression);
            pBitMapInfo->biSizeImage = SwapLong(pBitMapInfo->biSizeImage);
            pBitMapInfo->biXPelsPerMeter = SwapLong(pBitMapInfo->biXPelsPerMeter);
            pBitMapInfo->biYPelsPerMeter = SwapLong(pBitMapInfo->biYPelsPerMeter);
            pBitMapInfo->biClrUsed = SwapLong(pBitMapInfo->biClrUsed);
            pBitMapInfo->biClrImportant = SwapLong(pBitMapInfo->biClrImportant);
        }
        WriteBuffer(pBitMapInfo, BitmapSize);
        MyFree(pBitMapInfo);
    } else if (BitmapSize == sizeof(BITMAPCOREHEADER)) {
        nbits = bc.bcPlanes * bc.bcBitCount;

        /* old format */
        if (nbits == 24)
            cbColorTable = 0;
        else
            cbColorTable = (1 << nbits) * sizeof(RGBTRIPLE);

        if (fMacRsrcs) {
            bc.bcSize = SwapLong(bc.bcSize);
            bc.bcWidth = SwapWord(bc.bcWidth);
            bc.bcHeight = SwapWord(bc.bcHeight);
            bc.bcPlanes = SwapWord(bc.bcPlanes);
            bc.bcBitCount = SwapWord(bc.bcBitCount);
        }
        WriteBuffer(&bc, BitmapSize);
    } else {
        GenError1(2171); //"Unknown DIB header format"
    }

    if (cbColorTable) {
        pColorTable = (PCHAR) MyAlloc(cbColorTable);
        MyRead(infh, pColorTable, cbColorTable);
        WriteBuffer(pColorTable, cbColorTable);
        MyFree(pColorTable);
    }

    /* get the length of the bits */
    cbImage = MySeek(infh, 0L, SEEK_END) - BFOFFBITS(&bf) + BitmapSize + cbColorTable;

    /* seek to the beginning of the bits... */
    MySeek(infh, BFOFFBITS(&bf), SEEK_SET);

    return cbImage;
}

VOID
WriteOrdCode(
    void
    )
{
    WriteWord(0xFFFF);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  SetUpDlg() -                                                             */
/*                                                                           */
/*---------------------------------------------------------------------------*/

VOID
SetUpDlg(
    PDLGHDR pDlg,
    BOOL fDlgEx
    )
{
    if (fDlgEx) {
        // Hack -- this is how we version switch the dialog
        WriteWord(0x0001);          // store wDlgVer
        WriteWord(0xFFFF);          // store wSignature
        WriteLong(pDlg->dwHelpID);
        WriteLong(pDlg->dwExStyle); // store exstyle
    }

    /* write the style bits to the resource buffer */
    WriteLong(pDlg->dwStyle);   /* store style */

    if (!fDlgEx)
        WriteLong(pDlg->dwExStyle);   /* store exstyle */

    ItemCountLoc = CCount;        /* global marker for location of item cnt. */

    /* skip place for num of items */
    WriteWord(0);

    /* output the dialog position and size */
    WriteWord(pDlg->x);
    WriteWord(pDlg->y);
    WriteWord(pDlg->cx);
    WriteWord(pDlg->cy);

    /* output the menu identifier */
    if (pDlg->fOrdinalMenu) {
        WriteOrdCode();
        WriteWord((USHORT)wcsatoi(pDlg->MenuName));
    } else {
        WriteString(pDlg->MenuName, FALSE);
    }

    /* output the class identifier */
    if (pDlg->fClassOrdinal) {
        WriteOrdCode();
        WriteWord((USHORT)wcsatoi(pDlg->Class));
    } else {
        WriteString(pDlg->Class, FALSE);
    }

    /* output the title */
    WriteString(pDlg->Title, TRUE);

    /* add the font information */
    if (pDlg->pointsize) {
        WriteWord(pDlg->pointsize);
        if (fDlgEx) {
            WriteWord(pDlg->wWeight);
            WriteByte(pDlg->bItalic);
            WriteByte(pDlg->bCharSet);
        }
        WriteString(pDlg->Font, FALSE);
    }
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  SetUpItem() -                                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

VOID
SetUpItem(
    PCTRL LocCtl,
    BOOL fDlgEx
    )
{
    PWCHAR  tempptr;

    /* control dimensions, id, and style bits */
    WriteAlign();

    // control dimensions, id, and style bits
    if (fDlgEx) {
        WriteLong(LocCtl->dwHelpID);
        WriteLong(LocCtl->dwExStyle);
        WriteLong(LocCtl->dwStyle);
    } else {
        WriteLong(LocCtl->dwStyle);
        WriteLong(LocCtl->dwExStyle);
    }

    WriteWord(LocCtl->x);
    WriteWord(LocCtl->y);
    WriteWord(LocCtl->cx);
    WriteWord(LocCtl->cy);

    if (fDlgEx)
        WriteLong(LocCtl->id);
    else
        WriteWord(LOWORD(LocCtl->id));

    /* control class */
    tempptr = LocCtl->Class;
    if (*tempptr == 0xFFFF) {
        /* special class code follows */
        WriteWord(*tempptr++);
        WriteWord(*tempptr++);
    } else {
        WriteString(tempptr, FALSE);
    }

    /* text */
    if (LocCtl->fOrdinalText) {
        WriteOrdCode();
        WriteWord((USHORT)wcsatoi(LocCtl->text));
    } else {
        WriteString(LocCtl->text, TRUE);
    }

    if (fDlgEx)
        ItemExtraLoc = CCount;

    WriteWord(0);   /* zero CreateParams count */

    IncItemCount();

}


void
SetItemExtraCount(
    WORD wCount,
    BOOL fDlgEx
    )
{
    if (fDlgEx)
        *((WORD *) (CodeArray + ItemExtraLoc)) = wCount;
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  IncItemCount() -                                                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/

/* seemingly obscure way to increment # of items in a dialog */
/* ItemCountLoc indexes where we put the item count in the resource buffer, */
/* so we increment that counter when we add a control */

VOID
IncItemCount(
    VOID
    )
{
    PUSHORT     pus;

    pus = (PUSHORT)&CodeArray[ItemCountLoc];
    (*pus)++;
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  SwapItemCount() -                                                        */
/*                                                                           */
/*---------------------------------------------------------------------------*/
/* when writing a Mac resource fork, we need to swap this count before writing */
VOID
SwapItemCount(
    VOID
    )
{
    PUSHORT     pus;

    pus = (PUSHORT)&CodeArray[ItemCountLoc];
    *pus = SwapWord(*pus);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  FixMenuPatch() -                                                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/

VOID
FixMenuPatch(
    WORD wEndFlagLoc
    )
{
    if (fMacRsrcs)
        CodeArray[wEndFlagLoc + 1] |= MFR_END;
    else
        *((PWORD) (CodeArray + wEndFlagLoc)) |= MFR_END;
    // mark last menu item
//    CodeArray[wEndFlagLoc] |= MFR_END;
}


VOID
FixOldMenuPatch(
    WORD wEndFlagLoc
    )
{
    // mark last menu item
    if (fMacRsrcs)
        CodeArray[wEndFlagLoc + 1] |= OPENDMENU;
    else
        CodeArray[wEndFlagLoc] |= OPENDMENU;
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  MarkAccelFlagsByte() -                                                   */
/*                                                                           */
/*---------------------------------------------------------------------------*/

/* set the place where the accel end bit is going to be set */

VOID
MarkAccelFlagsByte (
    VOID
    )
{
    /* set the location to the current position in the resource buffer */
    mnEndFlagLoc = CCount;
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  PatchAccelEnd() -                                                        */
/*                                                                           */
/*---------------------------------------------------------------------------*/

VOID
PatchAccelEnd (
    VOID
    )
{
    if (fMacRsrcs)
        CodeArray[mnEndFlagLoc + 1] |= 0x80;
    else
        CodeArray[mnEndFlagLoc] |= 0x80;
}


// ----------------------------------------------------------------------------
//
//  SetUpMenu() -
//
// ----------------------------------------------------------------------------

WORD
SetUpMenu(
    PMENU pmn
    )
{
    WORD    wRes;

    WriteLong(pmn->dwType);
    WriteLong(pmn->dwState);
    WriteLong(pmn->dwID);

    // mark the last item added to the menu
    wRes = (WORD)CCount;

    WriteWord(pmn->wResInfo);
    WriteString(pmn->szText, TRUE);
    if (32)
        WriteAlign();
    if (pmn->wResInfo & MFR_POPUP)
        WriteLong(pmn->dwHelpID);

    return(wRes);
}


// ----------------------------------------------------------------------------
//
//  SetUpOldMenu() -
//
// ----------------------------------------------------------------------------

WORD
SetUpOldMenu(
    PMENUITEM mnTemp
    )
{
    WORD    wRes;

    /* mark the last item added to the menu */
    wRes = (WORD)CCount;

    /* write the menu flags */
    WriteWord(mnTemp->OptFlags);

    /* popup menus don't have id values */
    /* write ids of menuitems */
    if (!((mnTemp->OptFlags) & OPPOPUP))
        WriteWord(mnTemp->id);

    /* write text of selection */
    WriteString(mnTemp->szText, TRUE);

    return(wRes);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  GetRCData() -                                                          */
/*                                                                           */
/*---------------------------------------------------------------------------*/

WORD
GetRCData (
    PRESINFO pRes
    )
{
    PCHAR       pch, pchT;
    PWCHAR      pwch;
    WORD        nBytes = 0;
    ULONG       cb = 0;

    /* look for BEGIN (after id RCDATA memflags) */
    // 2134 -- "BEGIN expected in RCData"
    PreBeginParse(pRes, 2134);

    /* add the users data to the resource buffer until we see an END */
    while (token.type != END) {
    /* see explanation in rcl.c in GetStr() */
        if (token.type == LSTRLIT)
            token.type = token.realtype;

        switch (token.type) {
            case LSTRLIT:
                pwch = tokenbuf;
                while (token.val--) {
                    WriteWord(*pwch++);
                    nBytes += sizeof(WCHAR);
                }
                break;

            case STRLIT:
                cb = WideCharToMultiByte(uiCodePage, 0, tokenbuf,
                                            token.val, NULL, 0, NULL, NULL);
                pchT = pch = (PCHAR) MyAlloc(cb);
                WideCharToMultiByte(uiCodePage, 0, tokenbuf,
                                            token.val, pch, cb, NULL, NULL);
                while (cb--) {
                    WriteByte(*pch++);
                    nBytes += sizeof(CHAR);
                }
                MyFree(pchT);
                break;

            case NUMLIT:
                if (token.flongval) {
                    WriteLong(token.longval);
                    nBytes += sizeof(LONG);
                } else {
                    WriteWord(token.val);
                    nBytes += sizeof(WORD);
                }
                break;

            default:
                ParseError1(2164);
                return 0;
        }
        ICGetTok();
    }

    return(nBytes);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  AddFontRes() -                                                           */
/*                                                                           */
/*---------------------------------------------------------------------------*/

BOOL
AddFontRes(
    PRESINFO pRes
    )
{
    PFILE    fpFont;
    BYTE     font[FONT_ALL];
    PCHAR    pEnd, pDev, pFace;
    DWORD    offset;
    SHORT    nbyFont;
    PFONTDIR pFont;
    PFONTDIR pFontSearch;

    /* get handle to font file */
    fpFont = CtlFile(NULL_FILE);
    if (fpFont == NULL)
        return FALSE;
    MySeek(fpFont, 0L, SEEK_SET);

    /* copy font information to the font directory */
    /*    name strings are ANSI (8-bit) */
    MyRead(fpFont, &font[0], sizeof(ffh));
    pEnd = (PCHAR) (&font[0] + sizeof(ffh));    /* pointer to end of font buffer */
    offset = ((ffh * )(&font[0]))->dfDevice;
    if (offset != (LONG)0)  {
        MySeek(fpFont, (LONG)offset, SEEK_SET);        /* seek to device name */
        pDev = pEnd;
        do {
            MyRead(fpFont, pEnd, 1);              /* copy device name */
        } while (*pEnd++);
    } else {
        (*pEnd++ = '\0');
    }
    offset = ((ffh * )(&font[0]))->dfFace;
    MySeek(fpFont, (LONG)offset, SEEK_SET);         /* seek to face name */
    pFace = pEnd;
    do {                                /* copy face name */
        MyRead(fpFont, pEnd, 1);
    } while (*pEnd++);

    nbyFont = (SHORT)(pEnd - (PCHAR) &font[0]);

    pFont = (FONTDIR * )MyAlloc(sizeof(FONTDIR) + nbyFont);
    pFont->nbyFont = nbyFont;
    pFont->ordinal = pRes->nameord;
    pFont->next = NULL;
    memcpy((PCHAR)(pFont + 1), (PCHAR)font, nbyFont);

    if (!nFontsRead) {
        pFontList = pFontLast = pFont;
    } else {
        for (pFontSearch=pFontList ; pFontSearch!=NULL ; pFontSearch=pFontSearch->next) {
            if (pFont->ordinal == pFontSearch->ordinal) {
                SET_MSG(2181, curFile, token.row, pFont->ordinal);
                SendError(Msg_Text);
                MyFree(pFont);
                return FALSE;
            }
        }
        pFontLast = pFontLast->next = pFont;
    }

    /* rewind font file for SaveResFile() */
    MySeek(fpFont, 0L, SEEK_SET);
    return TRUE;
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  SaveResFile() -                                                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/


VOID
SaveResFile(
    PTYPEINFO pType,
    PRESINFO pRes
    )
{
    if (!fMacRsrcs)
        MyAlign(fhBin);

    AddResToResFile(pType, pRes, CodeArray, CCount, -1L);
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  GetNewIconsCursors(ResType)                                     */
/*                                                                          */
/*      This reads all the different forms of icons/cursors in 3.00 format  */
/*      in the input file                                                   */
/*                                                                          */
/*--------------------------------------------------------------------------*/

VOID
GetNewIconsCursors(
    PTYPEINFO pGroupType,
    PRESINFO pGroupRes,
    LPWSTR ResType
    )
{
    static SHORT  idIconUnique = 1;
    UINT          i;
    LONG          DescOffset;
    PTYPEINFO     pType;
    PRESINFO      pRes;
    NEWHEADER     NewHeader;
    DESCRIPTOR    Descriptor;
    BITMAPHEADER  BitMapHeader;
    RESDIR        ResDir;
    int           ArrayCount = 0;
    LOCALHEADER   LocHeader;

    /* Read the header of the bitmap file */
    MyRead(fhCode, &NewHeader, sizeof(NEWHEADER));

    /* Check if the file is in correct format */
    if ((NewHeader.Reserved != 0) || ((NewHeader.ResType != 1) && (NewHeader.ResType != 2)))
        GenError2(2175, tokenbuf); //"Resource file %ws is not in 3.00 format."
    /* Write the header into the Code array */
    WriteBuffer(&NewHeader, sizeof(NEWHEADER));

    /* Process all the forms one by one */
    for (i = 0; i < NewHeader.ResCount; i++) {
        /* Readin the Descriptor */
        MyRead(fhCode, &Descriptor, sizeof(DESCRIPTOR));

        /* Save the current offset */
        DescOffset = MySeek(fhCode, 0L, SEEK_CUR);

        /* Seek to the Data */
        MySeek(fhCode, Descriptor.OffsetToBits, SEEK_SET);

        /* Get the bitcount and Planes data */
        MyRead(fhCode, &BitMapHeader, sizeof(BITMAPHEADER));
        if (BitMapHeader.biSize != sizeof(BITMAPHEADER))
            GenError2(2176, tokenbuf); //"Old DIB in %ws.  Pass it through SDKPAINT."

        ResDir.BitCount = BitMapHeader.biBitCount;
        ResDir.Planes = BitMapHeader.biPlanes;

        /* Seek to the Data */
        MySeek(fhCode, Descriptor.OffsetToBits, SEEK_SET);

        ArrayCount = 0;

        /* fill the fields of ResDir and LocHeader */
        switch (NewHeader.ResType) {
            case CURSORTYPE:

                LocHeader.xHotSpot = Descriptor.xHotSpot;
                LocHeader.yHotSpot = Descriptor.yHotSpot;
                ArrayCount = sizeof(LOCALHEADER);

                ResDir.ResInfo.Cursor.Width = (USHORT)BitMapHeader.biWidth;
                ResDir.ResInfo.Cursor.Height = (USHORT)BitMapHeader.biHeight;

                break;

            case ICONTYPE:

                ResDir.ResInfo.Icon.Width = Descriptor.Width;
                ResDir.ResInfo.Icon.Height = Descriptor.Height;
                ResDir.ResInfo.Icon.ColorCount = Descriptor.ColorCount;
                /* The following line is added to initialise the unused
                 * field "reserved".
                 * Fix for Bug #10382 --SANKAR-- 03-14-90
                 */
                ResDir.ResInfo.Icon.reserved = Descriptor.reserved;
                break;

        }

        ResDir.BytesInRes = Descriptor.BytesInRes + ArrayCount;


        /* Create a pRes with New name */
        pRes = (PRESINFO) MyAlloc(sizeof(RESINFO));
        pRes->language = language;
        pRes->version = version;
        pRes->characteristics = characteristics;
        pRes ->name = NULL;
        pRes ->nameord = idIconUnique++;

        /* The individual resources must have the same memory flags as the
            ** group.
            */
        pRes ->flags = pGroupRes ->flags;
        pRes ->size = Descriptor.BytesInRes + ArrayCount;

        /* Create a new pType, or find existing one */
        pType = AddResType(NULL, ResType);


        /* Put Resource Directory entry in CodeArray */
        WriteBuffer(&ResDir, sizeof(RESDIR));

        /*
         * Write the resource name ordinal.
         */
        WriteWord(pRes->nameord);

        MyAlign(fhBin);

        AddResToResFile(pType, pRes, (PCHAR)&LocHeader, ArrayCount,
            Descriptor.BytesInRes);

        /* Seek to the Next Descriptor */
        MySeek(fhCode, DescOffset, SEEK_SET);
    }

    pGroupRes ->size = sizeof(NEWHEADER) + NewHeader.ResCount * (sizeof(RESDIR) + sizeof(SHORT));

    /* If the group resource is marked as PRELOAD, then we should use
        ** the same flags. Otherwise, mark it as DISCARDABLE
        */
    if (!(pGroupRes ->flags & NSPRELOAD))
        pGroupRes ->flags = NSMOVE | NSPURE | NSDISCARD;

    /* Close the input file, nothing more to read */
    fclose(fhCode);
    fhCode = NULL_FILE;

    /* Copy the code array into RES file for Group items */
    SaveResFile(pGroupType, pGroupRes);
}

/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  FileIsAnimated(LONG nbyFile)                                            */
/*                                                                          */
/*  This function checks to see if the file we have is 3.0 icon/cursor file */
/*  or an animated icon/cursor.                                             */
/*                                                                          */
/*  Returns RT_* of filetype.                                               */
/*--------------------------------------------------------------------------*/

DWORD
FileIsAnimated(
    LONG nbyFile
    )
{
    RTAG tag;
    LONG lRead;

    lRead = MyRead(fhCode, &tag, sizeof(RTAG));
    MySeek(fhCode, 0L, SEEK_SET);             /* return to start of file */
    if (lRead != sizeof(RTAG))
        return FALSE;

    return tag.ckID == FOURCC_RIFF;
}

/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  GetAniIconsAniCursors(ResType)                                          */
/*                                                                          */
/*  This function check if the file we have is a valid animated icon.       */
/*  All the work performed here is purelly optional and is done to make     */
/*  sure that the image we write in the res file is in the proper format.   */
/*  Just returning the nbyFile would be enough to copy the file in the res  */
/*  file.                                                                   */
/*                                                                          */
/*--------------------------------------------------------------------------*/

DWORD
GetAniIconsAniCursors(
    LONG nbyFile
    )
{
    RTAG tag;
    LONG lRead = nbyFile;

    /* Check if we have a RIFF file */
    lRead -= MyRead(fhCode, &tag, sizeof(RTAG));

    if( tag.ckID!=FOURCC_RIFF )
        GenError2(2173, tokenbuf);

    /* Read the next chunk */
    lRead -= MyRead(fhCode, &tag.ckID, sizeof(tag.ckID));
    if( tag.ckID!=FOURCC_ACON )
        GenError2(2173, tokenbuf);

    /* so we have an animated icon file, make sure all the blocks are there */
    while( MyRead(fhCode, &tag, sizeof(RTAG)) ) {
        lRead -= sizeof(RTAG)+tag.ckSize;
        MySeek(fhCode, tag.ckSize, SEEK_CUR);
    }

    if( lRead!=0 )
        GenError2(2173, tokenbuf);

    /*
     * Now that we are sure this is a valid file, move the
     * file pointer back at the begining of the file.
     */
    MySeek(fhCode, 0L, SEEK_SET);

    return nbyFile;
}

/*  GetBufferLen
 *      Returns the current length of the buffer
 */

WORD
GetBufferLen(
    VOID
    )
{
    return (WORD)CCount;
}


USHORT
GetItemCount(
    int Index
    )
{
    return *((USHORT UNALIGNED*)(CodeArray + Index));
}

void
SetItemCount(
    int Index,
    USHORT wCount
    )
{
    *((USHORT UNALIGNED*)(CodeArray + Index)) = wCount;
}

DWORD
SwapLong(
    DWORD dw
    )
{
    return ((dw << 24) & 0xff000000L) |
    ((dw << 8) & 0x00ff0000L) |
    ((dw >> 8) & 0x0000ff00L) |
    ((dw >> 24) & 0x000000ffL);
}

WORD
SwapWord(
    WORD w
    )
{
    return ((w << 8) & 0xff00) |
        ((w >> 8) & 0x00ff);
}

enum {
    itNone = -1,

    itIcn_ = 0,
    itIcl4 = 1,
    itIcl8 = 2,

    itIcs_ = 3,
    itIcs4 = 4,
    itIcs8 = 5,

    itIcm_ = 6,
    itIcm4 = 7,
    itIcm8 = 8,

    itMax = 9
};

static DWORD mpitres[itMax] = {
    'ICN#',
    'icl4',
    'icl8',
    'ics#',
    'ics4',
    'ics8',
    'icm#',
    'icm4',
    'icm8'
};

enum {
    fitIcn_ = 1 << itIcn_,
    fitIcl4 = 1 << itIcl4,
    fitIcl8 = 1 << itIcl8,

    fitIcs_ = 1 << itIcs_,
    fitIcs4 = 1 << itIcs4,
    fitIcs8 = 1 << itIcs8,

    fitIcm_ = 1 << itIcm_,
    fitIcm4 = 1 << itIcm4,
    fitIcm8 = 1 << itIcm8
};

void
GetMacIcon(
    TYPEINFO *pType,
    RESINFO *pRes
    )
{
    struct tagNEWHEADER gh;
    struct tagDESCRIPTOR ds;
    BITMAPINFOHEADER bmh;
    int ibDescNext;
    int mpitib[itMax];
    int it;
    int ires;
    int fitFound;

    /* The input file has already been opened; read in the bitmap file header */

    MyRead(fhCode, &gh, sizeof(struct tagNEWHEADER));
    if (gh.Reserved != 0 || gh.ResType != 1)
        GenError2(2175, tokenbuf);

    /* run through all the icons, keeping track of the useful ones */

    memset(mpitib, 0, sizeof(mpitib));
    ibDescNext = MySeek(fhCode, 0L, SEEK_CUR);
    fitFound = 0;
    for (ires = 0; ires < gh.ResCount; ires++) {
        /* Read in the descriptor */

        MySeek(fhCode, ibDescNext, SEEK_SET);
        MyRead(fhCode, &ds, sizeof(struct tagDESCRIPTOR));
        ibDescNext = MySeek(fhCode, 0L, SEEK_CUR);

        /* get bitmap header */

        MySeek(fhCode, ds.OffsetToBits, SEEK_SET);
        MyRead(fhCode, &bmh, sizeof(BITMAPINFOHEADER));
        if (bmh.biSize != sizeof(BITMAPINFOHEADER))
            GenError2(2176, tokenbuf);

        /* find valid color cases */

        if (bmh.biPlanes != 1)
            continue;
        if (bmh.biBitCount == 1)
            it = itIcn_;
        else if (bmh.biBitCount == 4)
            it = itIcl4;
        else if (bmh.biBitCount == 8)
            it = itIcl8;
        else
            continue;

        /* find valid sizes */

        if (bmh.biWidth == 16 && bmh.biHeight == 24)
            it += itIcm_ - itIcn_;
        else if (bmh.biWidth == 16 && bmh.biHeight == 32)
            it += itIcs_ - itIcn_;
        else if (bmh.biWidth == 32 && bmh.biHeight == 64)
            it += itIcn_ - itIcn_;
        else
            continue;

        /* mark sizes we found */

        fitFound |= 1 << it;
        mpitib[it] = ibDescNext - sizeof(struct tagDESCRIPTOR);
    }

    /* if no usable icon found, bail out */

    if (fitFound == 0) {
        GenWarning2(4508, tokenbuf);
    } else {
        if (fitFound & (fitIcn_|fitIcl4|fitIcl8))
            ProcessMacIcons(pRes, itIcn_, mpitib[itIcn_], mpitib[itIcl4], mpitib[itIcl8]);
        if (fitFound & (fitIcs_|fitIcs4|fitIcs8))
            ProcessMacIcons(pRes, itIcs_, mpitib[itIcs_], mpitib[itIcs4], mpitib[itIcs8]);
        if (fitFound & (fitIcm_|fitIcm4|fitIcm8))
            ProcessMacIcons(pRes, itIcm_, mpitib[itIcs_], mpitib[itIcs4], mpitib[itIcs8]);
    }

    fclose(fhCode);
    fhCode = NULL_FILE;
}


int
Luminance(
    const RGBColor* prgb
    )
{
    return prgb->red/256*30 + prgb->green/256*59 + prgb->blue/256*11;
}

/* threshold = middle gray */

#define rThreshold 128
#define gThreshold 128
#define bThreshold 128
int lumThreshold = rThreshold*30 + gThreshold*59 + bThreshold*11;


void
ProcessMacIcons(
    RESINFO* pResBase,
    int itBase,
    int ib1,
    int ib4,
    int ib8
    )
{
    BITMAPINFOHEADER bmh;
    RGBQUAD* rgq;
    struct tagDESCRIPTOR ds;
    BYTE* pBits;
    int cbWidth, cbMask;
    int cbBits;
    int ib, iq;
    int y;
    BYTE *pbIn;
    BYTE* pbOut;
    BYTE bIn = 0, bOut;

    /* create monochrome icon out of the best-looking icon */

    if (ib1 != 0) {
        /* read the DIB */

        ReadDIB(ib1, &ds, &bmh, &cbWidth, (void **) &pBits, &rgq, TRUE);

        /* invert bits, if color table is backwards */

        if (rgq[0].rgbReserved != 0)
            for (ib = cbWidth*bmh.biHeight/2; --ib >= 0; )
                pBits[ib] ^= 0xff;
    } else if (ib4 != 0) {
        /* read the DIB and create color-to-mono color mapping */

        ReadDIB(ib4, &ds, &bmh, &cbWidth, (void **) &pBits, &rgq, TRUE);
        for (iq = 0; iq < (int)bmh.biClrUsed; iq++)
            rgq[iq].rgbReserved =
                    Luminance(&rgcs16[rgq[iq].rgbReserved].rgb) < lumThreshold;

        /* map colors to black and white and convert to 1-bit/pixel */

        for (y = 0; y < (int)(bmh.biHeight/2); y++) {
            pbIn = pBits + y*cbWidth;
            pbOut = pbIn;
            assert(cbWidth % 4 == 0);   // we know it's 8 or 16 bytes wide
            for (ib = 0; ib < cbWidth; ) {
                bIn = *pbIn++;
                bOut = (bOut<<1) | rgq[bIn>>4].rgbReserved;
                bOut = (bOut<<1) | rgq[bIn&0xf].rgbReserved;
                ib++;
                if (ib % 4 == 0)
                    *pbOut++ = bOut;
            }
        }
    } else {
        /* read the DIB and create color-to-mono color mapping */

        ReadDIB(ib8, &ds, &bmh, &cbWidth, (void **) &pBits, &rgq, TRUE);
        for (iq = 0; iq < (int)bmh.biClrUsed; iq++)
            rgq[iq].rgbReserved =
                    Luminance(&rgcs256[rgq[iq].rgbReserved].rgb) < lumThreshold;

        /* map colors to black and white and convert to 1-bit/pixel */

        for (y = 0; y < (int)(bmh.biHeight/2); y++) {
            pbIn = pBits + y*cbWidth;
            pbOut = pbIn;
            assert(cbWidth % 8 == 0);   // we know it's 16 or 32 bytes wide
            for (ib = 0; ib < cbWidth; ) {
                bIn = *pbIn++;
                bOut = (bOut<<1) | rgq[bIn].rgbReserved;
                ib++;
                if (ib % 8 == 0)
                    *pbOut++ = bOut;
            }
        }
    }

    cbMask = (bmh.biWidth+31)/32*4;
    CompactAndFlipIcon(pBits, cbWidth, cbMask, bmh.biWidth/8, bmh.biWidth/8, bmh.biHeight/2);
    cbBits = bmh.biHeight * (bmh.biWidth/8);

    /* "xor" the mask back into image */
    pbOut = pBits; pbIn = pBits + cbBits/2;
    for (ib = cbBits/2; ib > 0; ib--)
        *pbOut++ ^= ~*pbIn++;

    /* and write out base icon */

    WriteMacRsrc(pBits, cbBits, pResBase, mpitres[itBase]);
    MyFree(pBits);
    MyFree(rgq);

    /* move over 16-color icon */

    if (ib4 != 0) {
        ReadDIB(ib4, &ds, &bmh, &cbWidth, (void **) &pBits, &rgq, TRUE);

        /* convert color table to mac standard palette */

        for (pbIn = pBits, ib = cbWidth*bmh.biHeight/2; ib > 0; pbIn++, ib--) {
            bIn = *pbIn;
            *pbIn = (rgq[bIn>>4].rgbReserved << 4) |
                    rgq[bIn&0x0f].rgbReserved;
        }

        /* compact and flip the image */

        cbMask = (bmh.biWidth+31)/32*4;
        CompactAndFlipIcon(pBits, cbWidth, cbMask, bmh.biWidth/2, bmh.biWidth/8, bmh.biHeight/2);
        cbBits = (bmh.biHeight/2) * (bmh.biWidth/2);

        /* "xor" the mask back into the image */

        pbOut = pBits; pbIn = pBits + cbBits;
        for (ib = 0; ib < cbBits; ib++, pbOut++) {
            if (ib % 4 == 0)
                bIn = *pbIn++;
            if ((bIn & 0x80) == 0)
                *pbOut ^= 0xf0;
            if ((bIn & 0x40) == 0)
                *pbOut ^= 0x0f;
            bIn <<= 2;
        }

        /* and write out the resource */

        WriteMacRsrc(pBits, cbBits, pResBase, mpitres[itBase+itIcs4-itIcs_]);
        MyFree(pBits);
        MyFree(rgq);
    }

    /* move over 256-color icon */

    if (ib8 != 0) {
        ReadDIB(ib8, &ds, &bmh, &cbWidth, (void **) &pBits, &rgq, TRUE);

        /* convert color table to mac standard palette */

        for (pbIn = pBits, ib = cbWidth*bmh.biHeight/2; ib > 0; pbIn++, ib--)
            *pbIn = rgq[*pbIn].rgbReserved;

        /* compact and flip the image */

        cbMask = (bmh.biWidth+31)/32*4;
        CompactAndFlipIcon(pBits, cbWidth, cbMask, bmh.biWidth, bmh.biWidth/8, bmh.biHeight/2);
        cbBits = (bmh.biHeight/2) * (bmh.biWidth);

        /* "xor" the mask back into the image */

        pbOut = pBits; pbIn = pBits + cbBits;
        for (ib = 0; ib < cbBits; ib++, pbOut++) {
            if (ib % 8 == 0)
                bIn = *pbIn++;
            if ((bIn & 0x80) == 0)
                *pbOut ^= 0xff;
            bIn <<= 1;
        }

        /* and write out the resource */

        WriteMacRsrc(pBits, cbBits, pResBase, mpitres[itBase+itIcs8-itIcs_]);

        MyFree(pBits);
        MyFree(rgq);
    }
}

void
WriteMacRsrc(
    void* pBits,
    int cbBits,
    RESINFO* pResBase,
    DWORD res
    )
{
    WCHAR sz[8];
    TYPEINFO* pType;
    RESINFO* pRes;

    sz[0] = (char)(res >> 24);
    sz[1] = (char)(res >> 16);
    sz[2] = (char)(res >> 8);
    sz[3] = (char)res;
    sz[4] = 0;
    pType = AddResType(sz, 0);

    pRes = (RESINFO *)MyAlloc(sizeof(RESINFO));
    *pRes = *pResBase;
    pRes->size = cbBits;

    AddResToResFile(pType, pRes, (PCHAR) pBits, (WORD)cbBits, 0);
}


void
CompactAndFlipIcon(
    BYTE* pBits,
    int cbRowCur,
    int cbRowMaskCur,
    int cbRowNew,
    int cbRowMaskNew,
    int Height
    )
{
    BYTE* pBitsNew;
    int y;
    BYTE* pbFrom, *pbTo;
    int cb;

    assert(cbRowCur >= cbRowNew);
    pBitsNew = (BYTE *) MyAlloc((WORD)(Height*(cbRowNew+cbRowMaskCur)));

    /* copy the bits over into the scratch space, compacting and
       flipping as we go */

    for (y = 0; y < Height; y++)
        memcpy(pBitsNew+y*cbRowNew, pBits+(Height-y-1)*cbRowCur, cbRowNew);

    /* copy over the mask, flipping and inverting as we go */

    for (y = 0; y < Height; y++) {
        pbTo = pBitsNew + cbRowNew*Height + y*cbRowMaskNew;
        pbFrom = pBits + cbRowCur*Height + (Height-y-1)*cbRowMaskCur;
        for (cb = cbRowMaskNew; cb > 0; cb--)
            *pbTo++ = ~*pbFrom++;
    }

    /* and move the result back to pBits */

    memcpy(pBits, pBitsNew, (cbRowNew+cbRowMaskCur)*Height);
    MyFree(pBitsNew);
}

void CrunchY(unsigned char* pbSrc, unsigned char* pbDst, int WidthBytes, int dySrc, int scale);
void CrunchX2(unsigned char* pbSrc, unsigned char* pbDst, int cbWidth, int dy);

void
GetMacCursor(
    TYPEINFO *pType,
    RESINFO *pRes
    )
{
    struct tagNEWHEADER gh;
    struct tagDESCRIPTOR ds;
    BITMAPINFOHEADER bmh;
    RGBQUAD *rgbq;
    short rgwMask[16];
    short rgwData[16];
    int xyBest;
    int xScale, yScale;
    char* pbBits;
    int ibDescNext, ibDescBest;
    int ires;
    int y, dy;
    int cbWidth;

    /* The input file has already been opened; read in the bitmap file header */

    MyRead(fhCode, &gh, sizeof(struct tagNEWHEADER));
    if (gh.Reserved != 0 || gh.ResType != 2)
        GenError2(2175, tokenbuf);

    /* find the best-looking cursor */

    xyBest = 32767;
    ibDescBest = -1;
    ibDescNext = MySeek(fhCode, 0L, SEEK_CUR);
    for (ires = 0; ires < gh.ResCount; ires++) {
        /* Read in the descriptor */

        MySeek(fhCode, ibDescNext, SEEK_SET);
        MyRead(fhCode, &ds, sizeof(struct tagDESCRIPTOR));
        ibDescNext = MySeek(fhCode, 0L, SEEK_CUR);

        /* get bitmap header */

        MySeek(fhCode, ds.OffsetToBits, SEEK_SET);
        MyRead(fhCode, &bmh, sizeof(BITMAPINFOHEADER));
        if (bmh.biSize != sizeof(BITMAPINFOHEADER))
            GenError2(2176, tokenbuf);
        /* !!! could we be smarter here about smaller cursors? */
        if (bmh.biBitCount != 1 || bmh.biPlanes != 1 ||
                bmh.biWidth % 16 != 0 || bmh.biHeight % 32 != 0)
            continue;
        xScale = bmh.biWidth / 16;
        yScale = bmh.biHeight / 32;
        if (xScale > 2)
            continue;
        if (xScale * yScale < xyBest) {
            xyBest = xScale * yScale;
            ibDescBest = ibDescNext - sizeof(struct tagDESCRIPTOR);
        }
    }

    /* if no usable cursor found, bail out */

    if (ibDescBest == -1) {
        GenWarning2(4507, tokenbuf);
        return;
    }

    /* go back and get the best descriptor and bitmap header */

    ReadDIB(ibDescBest, &ds, &bmh, &cbWidth, (void **) &pbBits, &rgbq, FALSE);

    /* if our color table is backwards, invert the bits */

    if ((rgbq[0].rgbRed == 0xff) &&
        (rgbq[0].rgbGreen == 0xff) &&
        (rgbq[0].rgbBlue == 0xff))
    {
        int cb;
        for (cb = cbWidth * bmh.biHeight; cb > 0; cb--)
            pbBits[cb] = ~pbBits[cb];
    }

    /* if necessary, scale the bits down to 16x16 */

    if (xyBest != 1) {
        GenWarning2(4506, tokenbuf);

        if (bmh.biWidth > 16) {
            assert(bmh.biWidth == 32);
            ds.xHotSpot /= (int)(bmh.biWidth / 16);
            CrunchX2((unsigned char *) pbBits, (unsigned char *) pbBits, cbWidth, bmh.biHeight);
            cbWidth = 2;
        }

        if (bmh.biHeight > 32) {
            ds.yHotSpot /= (int)(bmh.biHeight / 32);
            CrunchY((unsigned char *) pbBits, (unsigned char *) pbBits, cbWidth, bmh.biHeight, bmh.biHeight/32);
            bmh.biHeight = 32;
        }
    }

    /* now build the CURS resource mask and data */

    dy = bmh.biHeight/2;
    if (cbWidth == 1) {
        for (y = dy; y > 0; y--) {
            rgwMask[dy-y] = pbBits[y-1];
            rgwData[dy-y] = pbBits[dy+y-1] ^ ~rgwMask[dy-y];
        }
    } else {
        for (y = dy; y > 0; y--) {
            rgwMask[dy-y] = ~*(short*)&pbBits[(dy+y-1)*cbWidth];
            rgwData[dy-y] = *(short*)&pbBits[(y-1)*cbWidth] ^ rgwMask[dy-y];
        }
    }
    for (y = dy; y < 16; y++) {
        rgwMask[y] = 0;
        rgwData[y] = 0;
    }

    /* and write out the CURS resource data */

    WriteBuffer(rgwData, 32);
    WriteBuffer(rgwMask, 32);
    WriteWord(ds.yHotSpot);
    WriteWord(ds.xHotSpot);

    pRes->size = 32 + 32 + 2 + 2;

    /* and we're done - cleanup and return */

    MyFree(pbBits);
    MyFree(rgbq);
    fclose(fhCode);
    fhCode = NULL_FILE;
    AddResToResFile(pType, pRes, CodeArray, CCount, 0);
}


void
ReadDIB(
    int ibDesc,
    struct tagDESCRIPTOR* pds,
    BITMAPINFOHEADER* pbmh,
    int* pcbWidth,
    void** ppBits,
    RGBQUAD** prgq,
    BOOL fIcon
    )
{
    int cbBits;
    int iq;

    MySeek(fhCode, ibDesc, SEEK_SET);
    MyRead(fhCode, pds, sizeof(struct tagDESCRIPTOR));
    MySeek(fhCode, pds->OffsetToBits, SEEK_SET);
    MyRead(fhCode, pbmh, sizeof(BITMAPINFOHEADER));

    /* get the color table and map to macintosh color palette while we're
       looking at it */

    if (pbmh->biClrUsed == 0)
        pbmh->biClrUsed = 1 << pbmh->biBitCount;
    *prgq = (RGBQUAD *) MyAlloc(pbmh->biClrUsed * sizeof(RGBQUAD));
    MyRead(fhCode, *prgq, pbmh->biClrUsed * sizeof(RGBQUAD));
    switch (pbmh->biBitCount) {
        case 1:
            for (iq = 0; iq < (int)pbmh->biClrUsed; iq++)
                LookupIconColor(rgcs2, ccs2, &(*prgq)[iq]);
            break;
        case 4:
            for (iq = 0; iq < (int)pbmh->biClrUsed; iq++)
                LookupIconColor(rgcs16, ccs16, &(*prgq)[iq]);
            break;
        case 8:
            // !!! should use 256-color palette
            for (iq = 0; iq < (int)pbmh->biClrUsed; iq++)
                LookupIconColor(rgcs256, ccs256, &(*prgq)[iq]);
            break;
        default:
            break;
    }

    /* allocate space for the bits, and load them in */

    *pcbWidth = (pbmh->biBitCount*pbmh->biWidth+31)/32*4;
    if (fIcon)
        cbBits = (*pcbWidth * pbmh->biHeight/2) + ((pbmh->biWidth+31)/32*4) * (pbmh->biHeight/2);
    else
        cbBits = *pcbWidth * pbmh->biHeight;
    *ppBits = MyAlloc(cbBits);
    MyRead(fhCode, *ppBits, cbBits);
}


void
LookupIconColor(
    const ColorSpec* rgcs,
    int ccs,
    RGBQUAD* pq
    )
{
    int ics, icsBest;
    int dred, dgreen, dblue;
    int drgb, drgbBest;

    drgbBest = 32767;
    icsBest = -1;
    for (ics = 0; ics < ccs; ics++) {
        dred = pq->rgbRed - (rgcs[ics].rgb.red>>8);
        dgreen = pq->rgbGreen - (rgcs[ics].rgb.green>>8);
        dblue = pq->rgbBlue - (rgcs[ics].rgb.blue>>8);
        drgb = abs(dred) + abs(dgreen) + abs(dblue);
        if (drgb < drgbBest) {
            drgbBest = drgb;
            icsBest = ics;
            if (drgbBest == 0)
                break;
        }
    }
    pq->rgbReserved = (BYTE)rgcs[icsBest].value;
}


BOOL
IsIcon(
    TYPEINFO* ptype
    )
{
    unsigned long rt;
    short it;

    if (ptype->type == 0)
        return FALSE;
    rt = res_type(ptype->type[0], ptype->type[1], ptype->type[2], ptype->type[3]);
    for (it = 0; it < itMax; it++)
        if (rt == mpitres[it])
            return TRUE;
    return FALSE;
}


void
CrunchX2(
    unsigned char* pbSrc,
    unsigned char* pbDst,
    int cbWidth,
    int dy
    )
{
    unsigned short cw, cwWidth;
    unsigned short w;
    unsigned char b = 0;
    short bit;

    assert(dy > 0);
    assert(cbWidth > 1);

    cwWidth = cbWidth / 2;
    do {
        cw = cwWidth;
        do {
            w = (*pbSrc << 8)|(*(pbSrc+1));
            pbSrc += 2;
            bit = 8;
            do {
                b >>= 1;
                if ((w & 3) == 3)   /* if both are white, keep white */
                    b += 0x80;
                w >>= 2;
            } while (--bit != 0);
            *pbDst++ = b;
        } while (--cw > 0);
        pbDst += cwWidth & 1;
    } while (--dy > 0);
}


void
CrunchY(
    unsigned char* pbSrc,
    unsigned char* pbDst,
    int WidthBytes,
    int dySrc,
    int scale
    )
{
    int cbGroup;
    int cwRow;
    int dyDst, dy;
    unsigned short w;
    unsigned char *pb;

    if (scale <= 1) {
        memcpy(pbDst, pbSrc, dySrc * WidthBytes);
        return;
    }
    dyDst = dySrc / scale;
    cbGroup = WidthBytes * (scale - 1);

    do {
        cwRow = WidthBytes / sizeof(unsigned short);
        do {
            pb = pbSrc;
            w = *(unsigned short*)pb;
            dy = scale - 1;
            do {
                pb += WidthBytes;
                w &= *(unsigned short*)pb;
            } while (--dy > 0);
            *((unsigned short*)pbDst) = w;
            pbDst += sizeof(unsigned short);
            pbSrc += sizeof(unsigned short);
        } while (--cwRow > 0);
        pbSrc += cbGroup;
    } while (--dyDst > 0);
}

/*  WriteMacMap
 *
 *  Writes out a macintosh resource map from the type and resource
 *  data stashed away in the type and resource lists
 *
 *  See Inside Mac, Volume I, for a fine description of the
 *  format of a macintosh resource file
 */
void
WriteMacMap(
    void
    )
{
    TYPEINFO *ptype;
    RESINFO *pres;
    int i;
    size_t cch;
    int cbNameTbl, ctype, cref, ibName;
    long cbData;
    int offRef;
    WCHAR *pch;
#define cbMacType 8
#define cbMacRef 12

    /* alright, we're done reading all this stuff in, run through all
       our type lists and see what we've accumulated */

    cbData = MySeek(fhBin, 0L, 1) - MACDATAOFFSET;
    ctype = 0;
    cref = 0;
    cbNameTbl = 0;

    for (ptype = pTypInfo; ptype != 0; ptype = ptype->next) {
        if (ptype->nres == 0)
            continue;
        ctype++;
        cref += ptype->nres;
        for (pres = ptype->pres; pres != 0; pres = pres->next) {
            /* make sure each reference has a unique resource id */
            if (pres->nameord == 0)
                pres->nameord = (USHORT)IdUnique(ptype, pres);
            if (pres->name != 0)
                cbNameTbl += wcslen(pres->name)+1;
        }
    }

    /* write out the resource header at offset 0 in the file */

    MySeek(fhBin, 0L, 0);
    CtlInit();
    WriteLong((long)MACDATAOFFSET);
    WriteLong((long)MACDATAOFFSET + cbData);
    WriteLong(cbData);
    WriteLong((long)(16+4+2+2+2+2+2 + ctype*cbMacType + cref*cbMacRef + cbNameTbl));
    for (i = (MACDATAOFFSET - 16)/4; i-- > 0; )
        WriteLong(0);
    MyWrite(fhBin, CodeArray, CCount);

    /* we've already written out all the data, now write out the
       beginning of the map part of the resource file */

    MySeek(fhBin, (long)MACDATAOFFSET + cbData, 0);
    CtlInit();
    /* 24 bytes of 0s */
    for (i = 6; i-- > 0; )
        WriteLong(0);
    /* offset to start of type list */
    WriteWord(28);
    /* offset to start of name list */
    WriteWord((USHORT)(28 + 2 + ctype * cbMacType + cref * cbMacRef));

    /* dump out type table of the resource map */

    WriteWord((USHORT)(ctype - 1));
    offRef = 2 + ctype * cbMacType;
    for (ptype = pTypInfo; ptype != 0; ptype = ptype->next) {
        long rt;
        TYPEINFO *ptypeX;

        if (ptype->nres == 0)
            continue;

        /* 32-bit resource name - verify name truncation didn't
           cause conflicts */

        rt = MungeResType(ptype->type, ptype->typeord);

        for (ptypeX = ptype->next; ptypeX != 0; ptypeX = ptypeX->next) {
            if (rt == MungeResType(ptypeX->type, ptypeX->typeord)) {
                wchar_t szMac[8];
                wchar_t szType1[128];
                wchar_t szType2[128];

                szMac[0] = (BYTE) (rt >> 24);
                szMac[1] = (BYTE) (rt >> 16);
                szMac[2] = (BYTE) (rt >> 8);
                szMac[3] = (BYTE) (rt);
                szMac[4] = 0;

                if (ptype->typeord)
                    swprintf(szType1, L"%d", ptype->typeord);
                else
                    wcscpy(szType1, ptype->type);

                if (ptypeX->typeord)
                    swprintf(szType2, L"%d", ptypeX->typeord);
                else
                    wcscpy(szType2, ptypeX->type);

                GenWarning4(4509, szType1, szType2, szMac);
            }
        }
        WriteLong(rt);
        /* number of references of this type */
        WriteWord((USHORT)(ptype->nres-1));
        /*  offset to the reference list for this type */
        WriteWord((USHORT)offRef);
        offRef += ptype->nres * cbMacRef;
    }

    /* dump out reference table of the resource map */

    ibName = 0;
    for (ptype = pTypInfo; ptype != 0; ptype = ptype->next) {
        if (ptype->nres == 0)
            continue;

        for (pres = ptype->pres; pres != 0; pres = pres->next) {
            /* resource id */
            WriteWord(pres->nameord);
            /* offset to name in namelist */
            if (pres->name == 0) {
                WriteWord(0xffff);  /* unnamed, use -1 */
            } else {
                WriteWord((USHORT)ibName);
                ibName += wcslen(pres->name)+1;
            }
            /* attributes and resource data offset */
            WriteLong(pres->BinOffset);
            /* must be 0 */
            WriteLong(0L);
        }
    }

    /* and finally, dump out name table */

    /* note that we've implemented the Unicode=>ASCII conversion here by
       simply dumping out the low byte of each Unicode character. Effectively,
       we're assuming that resource names will be ASCII. Changing this would
       require changing the output code here and also changing a few places
       where we use wcslen to calculate the number of bytes that the ASCII
       resource name will require. If the resource name can contain 2-byte
       characters we would need to convert the Unicode to multi-byte and
       then count characters instead of just calling wcslen. */

    for (ptype = pTypInfo; ptype != 0; ptype = ptype->next) {
        if (ptype->nres == 0)
            continue;

        for (pres = ptype->pres; pres != 0; pres = pres->next) {
            if (pres->name == 0)
                continue;

            WriteByte(cch = wcslen(pres->name));
            for (pch = pres->name; cch--; )
                WriteByte((BYTE)*pch++);
        }
    }
    MyWrite(fhBin, CodeArray, CCount);
}


long
MungeResType(
    WCHAR *szType,
    short wOrd
    )
{
    long rt;
    int ich;

    switch (wOrd) {
        case 0:
            assert(szType != NULL && *szType != 0);
            rt = 0;
            for (ich = 0; ich < 4; ich++) {
                rt <<= 8;
                if (*szType)
                    rt |= (BYTE) (*szType++);
                else
                    rt |= ' ';
            }
            break;
        case RT_CURSOR:
            rt = 'CURS';
            break;
        case RT_BITMAP:
            rt = 'WBMP';
            break;
        case RT_ICON:
            rt = 'WICO';
            break;
        case RT_MENU:
            rt = 'WMNU';
            break;
        case RT_DIALOG:
            rt = 'WDLG';
            break;
        case RT_STRING:
            rt = 'STR#';
            break;
        case RT_ACCELERATOR:
            rt = 'WACC';
            break;
        case RT_RCDATA:
        case RT_DLGINIT:
            rt = 'HEXA';
            break;
        case RT_TOOLBAR:
            rt = 'TLBR';
            break;
        case RT_GROUP_CURSOR:
            rt = 'CURS';
            break;
        case RT_GROUP_ICON:
            rt = 'WGIC';
            break;
        case RT_VERSION:
            rt = 'WVER';
            break;
        case RT_FONTDIR:
        case RT_FONT:
        //case RT_ERRTABLE:
        //case RT_NAMETABLE:
        default: {
            static const char rgchHex[] = "0123456789ABCDEF";
            char ch4 = rgchHex[wOrd & 0x0f];
            char ch3 = rgchHex[(wOrd >> 4) & 0x0f];
            char ch2 = rgchHex[(wOrd >> 8) & 0x0f];
            char ch1 = 'M' + ((wOrd >> 12) & 0x0f);
            rt = res_type(ch1,ch2,ch3,ch4);
            break;
        }
    }

    return rt;
}


/*  IdUnique
 *
 *  Searches through the items of the given type looking for
 *  an unused resource id.  Returns the smallest resource id
 *  that is not currently used.
 *
 *  This routine handles icon families in a particular annoying
 *  way, using a particularly inefficient algorithm.  But it
 *  does keep icon ids synchronized if they have the same name.
 *
 *  Entry:
 *      ptype - type to search
 *      pres - resource type needing the unique id
 *
 *  Exit:
 *      retunrs - a unique resource id
 */
int
IdUnique(
    TYPEINFO *ptype,
    RESINFO *pres
    )
{
    int id;
    RESINFO *presScan;
    TYPEINFO* ptypeIcon;

    assert(ptype->pres != 0);

    if (IsIcon(ptype)) {
        /* see if we've already found an id for an icon with the same name */

        assert(pres->name != NULL);
        for (ptypeIcon = pTypInfo; ptypeIcon != NULL; ptypeIcon = ptypeIcon->next) {
            if (!IsIcon(ptypeIcon))
                continue;
            for (presScan = ptypeIcon->pres; presScan != NULL; presScan = presScan->next) {
                if (presScan->name == NULL || presScan->nameord == 0)
                    continue;
                if (wcscmp(presScan->name, pres->name) == 0)
                    return presScan->nameord;
            }
        }

        /* rats, didn't find it, gotta find one that's unique in *all* the
           icon types */

        for (id = idBase; ; ) {
            for (ptypeIcon = pTypInfo; ptypeIcon != NULL; ptypeIcon = ptypeIcon->next) {
                if (!IsIcon(ptypeIcon))
                    continue;
                for (presScan = ptypeIcon->pres; presScan != NULL; presScan = presScan->next) {
                    if (presScan->nameord == id)
                        goto NextId;
                }
            }
            return id;
NextId:
            id = (id+1) & 0xffff;
            if (id == 0)
                id = 1;
        }
    } else {
        for (id = idBase; ; ) {
            for (presScan = ptype->pres; presScan->nameord != id; ) {
                presScan = presScan->next;
                if (presScan == 0)
                    return id;
            }
            id = (id+1) & 0xffff;
            if (id == 0)
                id = 1;
        }
    }
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*      GetToolbar -                                                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/

SHORT
GetToolbarValue(
    void
    )
{
    SHORT sVal;

    if (!GetFullExpression(&sVal, GFE_ZEROINIT | GFE_SHORT))
        ParseError1(2250); //"expected numerical toolbar constant"

    return(sVal);
}

void
GetButtonSize(
    PSHORT cx,
    PSHORT cy
    )
{
    *cx= GetToolbarValue();
    if (token.type == COMMA)
        GetToken(TOKEN_NOEXPRESSION);
    *cy= GetToolbarValue();
}

int
GetToolbar(
    PRESINFO pRes
    )
{
    SHORT cx, cy;
    BOOL    bItemRead = FALSE;

    WriteWord(0x0001);  // Version 1 of this resource.

    GetButtonSize(&cx, &cy);

    WriteWord(cx);
    WriteWord(cy);

    ItemCountLoc = CCount;        /* global marker for location of item cnt. */

    /* skip place for num of items */
    WriteWord(0);

    PreBeginParse(pRes, 2251);

    while (token.type != END) {
        switch (token.type) {
            case TKSEPARATOR:
                bItemRead = TRUE;
                GetToken(TOKEN_NOEXPRESSION);
                WriteWord(0);
                break;

            case TKBUTTON:
                bItemRead = TRUE;
                GetToken(TRUE);
                if (token.type != NUMLIT)
                    ParseError1(2250); //"expected numerical toolbar constant"

                WriteSymbolUse(&token.sym);

                WriteWord(GetToolbarValue());
                break;

            case EOFMARK:
                ParseError1(2252); //"END expected in toolbar"
                quit(NULL);
                break;

            default:
                ParseError1(2253); //"unknown toolbar item type"
                GetToken(TOKEN_NOEXPRESSION);   // try to continue
                continue;
        }

        IncItemCount();
    }

    /* make sure we have a toolbar item */
    if (!bItemRead)
        ParseError1(2254); //"empty toolbars not allowed"

    if (fMacRsrcs)
        SwapItemCount();

    return (TRUE);
}
